package com.lagou.rpc.consumer.client;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;

@Component
public class RpcClientManger {
    private List<RpcClient> rpcClients = new ArrayList<>();

    @Autowired
    private ZkClient zkClient;

    private String clientPath;

    {
        try {
            clientPath = "/client/"+getRealIp();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @PostConstruct
    private void init() throws Exception {
        /*List<String> serverList = zkClient.getServerList();
        for (String s : serverList) {
            String[] connectString = s.split(":");
            RpcClient rpcClient = new RpcClient(connectString[0],Integer.valueOf(connectString[1]));
            rpcClients.add(rpcClient);
        }*/
        // 对节点server的子节点变化添加监听
        zkClient.createOrUpdatePath(clientPath);

        zkClient.setServerPathChildrenListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
                ChildData data = pathChildrenCacheEvent.getData();
                if(data == null){
                    return;
                }
                int lastIndex = data.getPath().lastIndexOf("/");
                String[] connectString = data.getPath().substring(lastIndex+1).split(":");
                if(connectString.length != 2){
                    return;
                }
                String ip = connectString[0];
                int port = Integer.valueOf(connectString[1]);
                switch (pathChildrenCacheEvent.getType()){
                    case CHILD_ADDED:
                        //服务器上线
                        System.out.println("server online host:"+ip+":"+port);
                        boolean match = rpcClients.stream().anyMatch(rpcClient -> rpcClient.getIp().equals(ip) && port == rpcClient.getPort());
                        if(!match) {
                            RpcClient rpcClient = new RpcClient(ip,port,zkClient);
                            rpcClients.add(rpcClient);
                        }
                        break;
                    case CHILD_REMOVED:
                        //服务器下线
                        System.out.println("server offline host:"+ip+":"+port);
                        Optional<RpcClient> rpcClient1 = rpcClients.stream().filter(rpcClient -> rpcClient.getIp().equals(ip) && port == rpcClient.getPort()).findFirst();
                        if(rpcClient1.isPresent()){
                            rpcClients.remove(rpcClient1.get());
                        }
                        break;
                }
            }
        });
    }

    public RpcClient getRpcClient() throws Exception {
        CuratorFramework curatorFramework = zkClient.getCuratorFramework();
        /*List<String> serverList = curatorFramework.getChildren().forPath(clientPath);
        if(serverList.isEmpty()){
            return rpcClients.get(0);
        }*/
        long minResponseTime = Integer.MAX_VALUE;
        String fastNode = null;
        for (RpcClient rpcClient : rpcClients) {
            String node = rpcClient.getIp()+":"+rpcClient.getPort();
            Stat stat = curatorFramework.checkExists().forPath(clientPath + "/" + node);
            long responseTime = 0;
            if(stat == null){
                responseTime = 0;
                fastNode = node;
                break;
            }
            byte[] bytes = curatorFramework.getData().forPath(clientPath+"/"+node);
            String str = new String(bytes, Charset.forName("UTF-8"));
            responseTime = str.isEmpty() ? 0 : Long.valueOf(str.split(":")[0]);
            if(minResponseTime > responseTime){
                minResponseTime = responseTime;
                fastNode = node;
            }
        }

        String[] connectString = fastNode.split(":");
        String ip = connectString[0];
        int port = Integer.valueOf(connectString[1]);

        Optional<RpcClient> optional = rpcClients.stream().filter(rpcClient -> rpcClient.getIp().equals(ip) && rpcClient.getPort() == port).findFirst();

        if(optional.isPresent()){
            return optional.get();
        }else {
            return null;
        }
    }

    public void close(){
        for (RpcClient rpcClient : rpcClients) {
            rpcClient.close();
        }
    }

    public String getRealIp() throws SocketException {
        String localip = null;// 本地IP，如果没有配置外网IP则返回它
        String netip = null;// 外网IP
        Enumeration<NetworkInterface> netInterfaces =
                NetworkInterface.getNetworkInterfaces();
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP
        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress()
                        && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
                    netip = ip.getHostAddress();
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress()
                        && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
                    localip = ip.getHostAddress();
                }
            }
        }
        if (netip != null && !"".equals(netip)) {
            return netip;
        } else {
            return localip;
        }
    }
}
